package cn.isjinhao.se.sql.grammar.statement;

import cn.isjinhao.se.sql.function.evaluator.FunctionEvaluator;
import cn.isjinhao.se.sql.function.handler.date.format.DateConst;
import cn.isjinhao.se.sql.grammar.CurdStatement;
import cn.isjinhao.se.sql.grammar.ErrorReportableCurdStatementParser;
import cn.isjinhao.se.sql.grammar.GrammarErrorReporterDelegate;
import cn.isjinhao.se.sql.grammar.statement.create.InsertParser;
import cn.isjinhao.se.sql.grammar.statement.delete.DeleteParser;
import cn.isjinhao.se.sql.grammar.statement.retrieve.SelectParser;
import cn.isjinhao.se.sql.grammar.statement.retrieve.SingleSelectType;
import cn.isjinhao.se.sql.grammar.statement.retrieve.represention.Select;
import cn.isjinhao.se.sql.grammar.statement.retrieve.represention.SingleSelect;
import cn.isjinhao.se.sql.grammar.statement.update.UpdateParser;
import cn.isjinhao.se.sql.lexical.scan.TokenSequence;
import cn.isjinhao.se.sql.lexical.token.Token;
import cn.isjinhao.se.sql.lexical.token.TokenType;

import java.util.HashSet;
import java.util.Set;

/**
 * @Author ISJINHAO
 * @Date 2021/7/22 19:16
 */
public abstract class AbstractCurdStatementParser extends ErrorReportableCurdStatementParser {

    protected final TokenSequence tokenSequence;

    private final FunctionEvaluator functionEvaluator;

    private final Set<String> functionNameSet = new HashSet<>();

    protected AbstractCurdStatementParser(TokenSequence tokenSequence, FunctionEvaluator functionEvaluator) {
        super(new GrammarErrorReporterDelegate(tokenSequence));
        this.tokenSequence = tokenSequence;
        this.functionEvaluator = functionEvaluator;
        functionNameSet.addAll(functionEvaluator.functionNameSet());
    }

    protected void saveSingleSelectContext(CurdStatement curdStatement, CurdStatement parent, SingleSelectType type) {
        if (curdStatement instanceof SingleSelect) {
            SingleSelect singleSelect = (SingleSelect) curdStatement;
            if (singleSelect.getSingleSelectType() == null || singleSelect.getSingleSelectType() == SingleSelectType.UNDETERMINED) {
                singleSelect.setSingleSelectType(type);
                singleSelect.setParentSelectStatement(parent);
            }
        } else if (curdStatement instanceof Select) {
            Select select = (Select) curdStatement;
            saveSingleSelectContext(select.getLeftSelectStatement(), select, type);
            saveSingleSelectContext(select.getRightSelectStatement(), select, type);
        } else if (curdStatement instanceof BinaryArithmetic) {
            BinaryArithmetic binaryArithmetic = (BinaryArithmetic) curdStatement;
            saveSingleSelectContext(binaryArithmetic.getLeftSelectStatement(), binaryArithmetic, type);
            saveSingleSelectContext(binaryArithmetic.getRightSelectStatement(), binaryArithmetic, type);
        } else if (curdStatement instanceof UnaryArithmetic) {
            UnaryArithmetic unaryArithmetic = (UnaryArithmetic) curdStatement;
            saveSingleSelectContext(unaryArithmetic.getSelectStatement(), unaryArithmetic, type);
        } else if (curdStatement instanceof Comparison) {
            Comparison comparison = (Comparison) curdStatement;
            saveSingleSelectContext(comparison.getLeftSelectStatement(), comparison, type);
            saveSingleSelectContext(comparison.getRightSelectStatement(), comparison, type);
        }
    }


    protected void doFunctionStaticCheck(Function function) {
        Class<? extends AbstractCurdStatementParser> aClass = this.getClass();

        if (aClass.isAssignableFrom(SelectParser.class)) {
            functionEvaluator.staticCheck(function, CurdStatementType.SELECT);
        } else if (aClass.isAssignableFrom(UpdateParser.class)) {
            functionEvaluator.staticCheck(function, CurdStatementType.UPDATE);
        } else if (aClass.isAssignableFrom(InsertParser.class)) {
            functionEvaluator.staticCheck(function, CurdStatementType.INSERT);
        } else if (aClass.isAssignableFrom(DeleteParser.class)) {
            functionEvaluator.staticCheck(function, CurdStatementType.DELETE);
        } else if (aClass.isAssignableFrom(StatementParser.class)) {
            functionEvaluator.staticCheck(function, CurdStatementType.DELETE);
        }

    }

    protected FunctionEvaluator getFunctionEvaluator() {
        return functionEvaluator;
    }

    protected boolean checkFunction(Token current, Token next) {
        if (current == null || next == null) {
            return false;
        }
        return checkFunctionName(current) && TokenType.LEFT_PAREN.equals(next.getType());
    }

    protected boolean checkTimeUnit(Token current, Token next) {
        if (current == null || next == null) {
            return false;
        }
        return (DateConst.YEAR.equals(current) || DateConst.MONTH.equals(current) || DateConst.DAY.equals(current)
                || DateConst.HOUR.equals(current) || DateConst.MINUTE.equals(current)
                || DateConst.SECOND.equals(current) || DateConst.MICROSECOND.equals(current)) && TokenType.FROM.equals(next.getType());
    }

    private boolean checkFunctionName(Token token) {
        if (token == null) {
            return false;
        }
        return TokenType.IDENTIFIER.equals(token.getType()) && functionNameSet.contains((String) token.getLiteral());
    }

}
